﻿/**
 * A CLIK Window component that can be used to display forms constructed of Flash content / CLIK components.
 * 
 * Buttons are the foundation component of the CLIK framework and are used anywhere a clickable interface control is required. The default Button class (gfx.controls.Button) supports a textField to display a label, and states to visually indicate user interaction. Buttons can be used on their own, or as part of a composite component, such as ScrollBar arrows or the Slider thumb. Most interactive components that are click-activated compose or extend Button.
    
    The CLIK Button is a general purpose button component, which supports mouse interaction, keyboard interaction, states and other functionality that allow it to be used in a multitude of user interfaces. It also supports toggle capability as well as animated states. The ToggleButton, AnimatedButton and AnimatedToggleButton provided in the Button.fla component source file all use the same base component class.
    
    <b>Inspectable Properties</b>
    The inspectable properties of the Button component are:
    <ul>
        <li><i>enabled</i>: Disables the component if set to false.</li>
        <li><i>contentPadding</i>: The top, bottom, left, and right padding that should be applied to the content loaded into the Window.</li>
        <li><i>minWidth</i>: The minimum width of the Window when it is resized via the resize Button.</li>
        <li><i>maxWidth</i>: The maximum width of the Window when it is resized via the resize Button.</li>
        <li><i>minHeight</i>: The minimum height of the Window when it is resized via the resize Button.</li>
        <li><i>maxHeight</i>: The maximum height of the Window when it is resized via the resize Button.</li>
        <li><i>source</i>: The export name of the symbol that should be loaded into the Window.</li>
        <li><i>visible</i>: Hides the component if set to false.</li>
    </ul>
    
    <b>States</b>
    The CLIK Window has no states as it loads and displays external content.
    
    <b>Events</b>
    All event callbacks receive a single Event parameter that contains relevant information about the event. The following properties are common to all events. <ul>
    <li><i>type</i>: The event type.</li>
    <li><i>target</i>: The target that generated the event.</li></ul>
        
    The events generated by the Button component are listed below. The properties listed next to the event are provided in addition to the common properties.
    <ul>
        <li><i>ComponentEvent.SHOW</i>: The visible property has been set to true at runtime.</li>
        <li><i>ComponentEvent.HIDE</i>: The visible property has been set to false at runtime or the window has been closed.</li>
    </ul>
 */
    
/**************************************************************************

Filename    :   Window.as

Copyright   :   Copyright 2011 Autodesk, Inc. All Rights reserved.

Use of this software is subject to the terms of the Autodesk license
agreement provided at the time of installation or download, or which
otherwise accompanies this software in either electronic or hard copy form.

**************************************************************************/

package scaleform.clik.controls {
        
    import flash.display.DisplayObject;
    import flash.display.MovieClip;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.utils.getDefinitionByName;
    import scaleform.clik.events.ResizeEvent;
    
    import scaleform.clik.constants.InvalidationType;
    import scaleform.clik.constants.ConstrainMode;
    import scaleform.clik.core.UIComponent;
    import scaleform.clik.controls.Button;
    import scaleform.clik.events.ComponentEvent;
    import scaleform.clik.utils.Constraints;
    import scaleform.clik.utils.ConstrainedElement;
    import scaleform.clik.utils.Padding;
    
    public class Window extends UIComponent {
        
    // Constants:
        
    // Public Properties:
        /** The minimum width of the Window when it is resized via the resize Button. */
        [Inspectable(defaultValue=150)]
        public var minWidth:Number = 150;
        /** The maximum width of the Window when it is resized via the resize Button. */
        [Inspectable(defaultValue=500)]
        public var maxWidth:Number = 500;
        /** The minimum height of the Window when it is resized via the resize Button. */
        [Inspectable(defaultValue=150)]
        public var minHeight:Number = 150;
        /** The maximum height of the Window when it is resized via the resize Button. */
        [Inspectable(defaultValue=500)]
        public var maxHeight:Number = 500;
    
    // Protected Properties:
        protected var _title:String;
        protected var _src:String = "";
        protected var _contentPadding:Padding;
        protected var _content:DisplayObject;
        protected var _dragProps:Array;
        
    // UI Elements:
        public var closeBtn:Button;
        public var okBtn:Button;
        public var resizeBtn:Button;
        public var titleBtn:Button;
        public var background:MovieClip;
        public var hit:MovieClip;
        
    // Initialization:
        public function Window() {
            super();
            
            _contentPadding = new Padding(0, 0, 0, 0);
            hitArea = hit;
        }
        
    // Public Methods:
        [Inspectable(defaultValue="My Window")]
        public function get title():String     { return _title; }
        public function set title(value:String):void {
            _title = value;
            if (titleBtn.initialized) { titleBtn.label = _title; }
        }
        
        [Inspectable(defaultValue="")]
        public function get source():String { return _src; }
        public function set source(value:String):void {
            _src = value;
            invalidate("source");
            // TODO: Dynamic loading of new source (only supports one time load currently; see configUI)
        }
        
        [Inspectable(name = "contentPadding", defaultValue = "top:0,right:0,bottom:0,left:0")]
        public function get contentPadding():Object { return _contentPadding; }
        public function set contentPadding(value:Object):void {
            _contentPadding = new Padding(value.top, value.right, value.bottom, value.left);
            invalidate("padding");
        }
        
    // Protected Methods:
        override protected function preInitialize():void {
            constraints = new Constraints(this, ConstrainMode.REFLOW);
        }
        
        override protected function initialize():void {
            tabEnabled = false; // Components with a TextField can not be tabEnabled, otherwise they will get tab focus separate from the TextField.
            mouseEnabled = mouseChildren = enabled;
            super.initialize();
        }
        
        override protected function configUI():void {
            initSize();
            
            if (hitArea != null) { constraints.addElement("hitArea", hitArea, Constraints.ALL); }
            if (background != null) { constraints.addElement("background", background, Constraints.ALL); }
            
            if (titleBtn != null) {
                titleBtn.label = _title || "My Window";
                titleBtn.addEventListener(MouseEvent.MOUSE_DOWN, onWindowStartDrag, false, 0, true);
                constraints.addElement("titleBtn", titleBtn, Constraints.TOP | Constraints.LEFT | Constraints.RIGHT);
            }
            if (closeBtn != null) {
                closeBtn.addEventListener(MouseEvent.CLICK, onCloseButtonClick, false, 0, true);
                constraints.addElement("closeBtn", closeBtn, Constraints.TOP | Constraints.RIGHT);
            }
            if (resizeBtn != null) {
                constraints.addElement("resizeBtn", resizeBtn, Constraints.BOTTOM | Constraints.RIGHT);
                resizeBtn.addEventListener(MouseEvent.MOUSE_DOWN, onResizeStartDrag, false, 0, true);
            }
            if (okBtn != null) {
                constraints.addElement("okBtn", okBtn, Constraints.BOTTOM | Constraints.RIGHT);
                okBtn.addEventListener(MouseEvent.CLICK, onCloseButtonClick, false, 0, true);
            }
        }
        
        override protected function draw():void {
            if (isInvalid("source")) {
                loadSource();
                reflowContent();
            } else if (isInvalid("padding")) {
                reflowContent();
            }
        
            // Resize and update constraints
            if (isInvalid(InvalidationType.SIZE)) {
                constraints.update(_width, _height);
            }
        }
        
        protected function loadSource():void {
            // TODO: Support external sources (swfs, images); only supports symbols currently
            if (_src != "") {
                if (_content) { 
                    constraints.removeElement("content");
                    removeChild(_content); 
                }
                
                var classRef:Class = getDefinitionByName(_src) as Class;
                if (classRef) { _content = new classRef(); }
                else {
                    _content = null;
                    trace("Error: Cannot load content for " + name + "; symbol " + _src + " not found!"); 
                    return;
                }
                addChild(_content);
                constraints.addElement("content", _content, Constraints.ALL);
                _content.name = "content";
            }
        }
        
        protected function reflowContent():void {
            if (!_content) return;
            
            var p:Padding = _contentPadding;
            var element:ConstrainedElement = constraints.getElement("content");
            _content.x = element.left = p.left;
            _content.y = element.top = p.top;
            element.right = p.right;
            element.bottom = p.bottom;
            
            _content.width = _width - p.horizontal;
            _content.height = _height - p.vertical;
            
            // TODO: Add properties to define content area (by border offset)
            // TODO: Add properties to define whether content is auto fit or window is auto fit
            
            invalidateSize();
        }
        
        protected function onWindowStartDrag(e:Event):void {
            stage.addEventListener(MouseEvent.MOUSE_UP, onWindowStopDrag, false, 0, true);
            startDrag();
        }
        
        protected function onWindowStopDrag(e:Event):void {
            stage.removeEventListener(MouseEvent.MOUSE_UP, onWindowStopDrag, false);
            stopDrag();
        }
        
        protected function onResizeStartDrag(e:Event):void {
            stage.addEventListener(MouseEvent.MOUSE_UP, onResizeStopDrag, false, 0, true);
            _dragProps = [parent.mouseX - (x + width), parent.mouseY - (y + height)];
            stage.addEventListener(MouseEvent.MOUSE_MOVE, onResizeMouseMove, false, 0, true);
        }
        
        protected function onResizeStopDrag(e:Event):void {
            stage.removeEventListener(MouseEvent.MOUSE_MOVE, onResizeMouseMove, false);
            stage.removeEventListener(MouseEvent.MOUSE_UP, onResizeStopDrag, false);
        }
        
        protected function onResizeMouseMove(e:Event) {
            var w:Number = Math.max(minWidth, Math.min(maxWidth, parent.mouseX - x - _dragProps[0]));
            var h:Number = Math.max(minHeight, Math.min(maxHeight, parent.mouseY - y - _dragProps[1]));
            if (w != _width || h != _height) {
                setSize(w, h);
                dispatchEvent( new ResizeEvent(ResizeEvent.RESIZE, scaleX, scaleY) );
                //validateNow();
            } else {
                // trace("Mouse move called: parent.mouseX=" + parent.mouseX + ", parent.mouseY=" + parent.mouseY);
            }
        }
        
        protected function onCloseButtonClick(e:MouseEvent):void {
            parent.removeChild(this);
            dispatchEvent(new ComponentEvent(ComponentEvent.HIDE));
        }
    }
}
